# app.py

import os
import logging
from flask import Flask, request, jsonify, render_template
from models import db, WebhookEvent, InvalidCommit, Auth, User
from config import Config
from datetime import datetime
import utils
import re

app = Flask(__name__)
app.config.from_object(Config)

# 初始化数据库
db.init_app(app)

# 设置日志记录
if not os.path.exists('logs'):
    os.makedirs('logs')

logging.basicConfig(
    filename=Config.LOG_FILE,
    level=logging.INFO,
    format='[%(asctime)s] %(levelname)s: %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logger = logging.getLogger(__name__)

# DCO 正则表达式
DCO_REGEX = re.compile(r'^Signed-off-by:\s+([^<]+)<([^>]+)>$', re.IGNORECASE)

# OAuth2 回调处理
@app.route('/oauth2_callback', methods=['GET'])
def oauth2_callback():
    try:
        # 从查询参数中获取数据
        code = request.args.get('code')  # 获取 Git 提供的 authorization code
        setup_action = request.args.get('setup_action')  # 获取 Git 提供的 setup_action 参数
        installation_id = request.args.get('installation_id')  # 获取 Git 提供的 installation_id 参数
        
        if not code:
            logger.error("Authorization code is missing")
            return jsonify({"error": "Missing authorization code"}), 400

        logger.info(f"Received code: {code}")

        # 你可以继续调用获取token的函数
        TOKEN_URL = "https://api.atomgit.com/login/oauth/access_token"
        token_response = utils.get_token(TOKEN_URL, client_id=Config.OAUTH2_CLIENT_ID, client_secret=Config.OAUTH2_CLIENT_SECRET, code=code)
        logger.info(f"token Response: {token_response}")
        
        if token_response.get("error"):
            logger.error(f"Error fetching token: {token_response['error']}")
            return jsonify({"error": token_response["error"]}), 400
        
        # 处理 token 获取和后续逻辑
        access_token = token_response["access_token"]
        logger.info(f"Access Token: {access_token}")

        # 使用 access_token 获取用户信息并做后续处理
        user_info = utils.get_user_info(access_token) 
        logger.info(f"User Info: {user_info}")

       # 检查用户信息是否有效
        if user_info and 'id' in user_info:
            user_id = user_info['id']
            login = user_info.get('login')
            name = user_info.get('name')
            email = user_info.get('email')
            phone = user_info.get('phone', '')  # 如果没有则为默认空字符串
            avatar_url = user_info.get('avatar_url', '')  # 用户头像链接
            bio = user_info.get('bio', '')  # 用户简介
            company = user_info.get('company', '')  # 用户公司
            location = user_info.get('location', '')  # 用户地点
            blog = user_info.get('blog', '')  # 用户博客链接
            followers = user_info.get('followers', 0)  # 粉丝数
            following = user_info.get('following', 0)  # 关注数
            html_url = user_info.get('html_url', '')  # 用户网页链接
            owned_private_repos = user_info.get('owned_private_repos', 0)  # 私有仓库数
            public_repos = user_info.get('public_repos', 0)  # 公开仓库数
            total_private_repos = user_info.get('total_private_repos', 0)  # 私有仓库总数
            total_repos = user_info.get('total_repos', 0)  # 总仓库数
            url = user_info.get('url', '')  # 个人主页 URL
            hide_email = user_info.get('hide_email', 0)  # 隐藏邮箱标识（0 或 1）

            # 将用户信息存入数据库
            existing_user = User.query.filter_by(id=user_id).first()
            
            if existing_user:
                # 如果用户已存在，更新用户信息
                existing_user.login = login
                existing_user.name = name
                existing_user.email = email
                existing_user.phone = phone
                existing_user.avatar_url = avatar_url
                existing_user.bio = bio
                existing_user.company = company
                existing_user.location = location
                existing_user.blog = blog
                existing_user.followers = followers
                existing_user.following = following
                existing_user.html_url = html_url
                existing_user.owned_private_repos = owned_private_repos
                existing_user.public_repos = public_repos
                existing_user.total_private_repos = total_private_repos
                existing_user.total_repos = total_repos
                existing_user.url = url
                existing_user.hide_email = hide_email
                existing_user.updated_at = datetime.utcnow()
                db.session.commit()
                logger.info(f"Updated existing user {user_id}")
            else:
                # 如果用户不存在，插入新用户
                new_user = User(
                    id=user_id,
                    login=login,
                    name=name,
                    email=email,
                    phone=phone,
                    avatar_url=avatar_url,
                    bio=bio,
                    company=company,
                    location=location,
                    blog=blog,
                    followers=followers,
                    following=following,
                    html_url=html_url,
                    owned_private_repos=owned_private_repos,
                    public_repos=public_repos,
                    total_private_repos=total_private_repos,
                    total_repos=total_repos,
                    url=url,
                    hide_email=hide_email
                )
                db.session.add(new_user)
                db.session.commit()
                logger.info(f"Inserted new user {user_id}")
            
            # 将授权信息存入 Auth 表
            auth = Auth(
                user_id=user_id, 
                installation_id=installation_id,
                access_token=access_token,
                refresh_token=token_response.get('refresh_token', ''),
                expires_in=token_response.get('expires_in', 0),  # 访问令牌的过期时间
                refresh_token_expires_in=token_response.get('refresh_token_expires_in', 0)  # 刷新令牌的过期时间
            )
            db.session.add(auth)
            db.session.commit()
            logger.info(f"Stored access token for user {user_id}")
        else:
            logger.error("Invalid user information received")
            return jsonify({"error": "Failed to retrieve valid user information"}), 400

        return render_template('oauth_success.html'), 200

    except Exception as e:
        logger.error(f"Error in OAuth2 callback: {str(e)}")
        return jsonify({"error": "An error occurred during OAuth2 callback"}), 500

# 合并事件处理路由
@app.route('/merge_hook', methods=['POST'])
def merge_hook():
    try:
        # 获取请求体中的数据
        req = request.get_json()
        if not req:
            return jsonify({"error": "Invalid payload"}), 400

        logger.info(f"Received merge hook: {req}")

        # 处理合并事件
        response = utils.handle_merge_event(req)
        if response.get("code") == 0:
            return jsonify(response), 200
        else:
            return jsonify(response), 400

    except Exception as e:
        logger.error(f"Error in merge hook: {str(e)}")
        return jsonify({"error": "An error occurred during merge hook processing"}), 500

@app.route('/webhook', methods=['POST'])
def webhook():
    headers = request.headers
    signature = headers.get('X-Atomgit-Token', '')

    # 验证签名
    if Config.WEBHOOK_SECRET and signature != Config.WEBHOOK_SECRET:
        logger.warning('Webhook 签名验证失败。')
        return jsonify({'message': 'Unauthorized'}), 401

    event_type = headers.get('X-Atomgit-Event', '')
    logger.info(f'处理事件类型: {event_type}')

    if event_type.lower() != 'push':
        logger.info('事件类型不处理。')
        return jsonify({'message': 'Event not handled'}), 200

    data = request.get_json()
    if not data:
        logger.error('解析 Webhook 负载失败：无有效 JSON。')
        return jsonify({'message': 'Invalid payload'}), 400

    logger.info(f'Webhook Payload: {data}')

    # 获取推送信息
    repository = data.get('repository', {})
    owner = repository.get('owner', {}).get('name', '')
    repo = repository.get('name', '')
    commits = data.get('commits', [])
    pusher = commits[0].get('author', {}).get('name', '') if commits else ''
    ref = data.get('ref', '')
    before = data.get('before', '')
    after = data.get('after', '')

    if not owner or not repo or not commits:
        logger.error('缺少必要的仓库或提交信息。')
        return jsonify({'message': 'Missing information'}), 400

    logger.info(f'处理仓库: {owner}/{repo}, Pusher: {pusher}, Ref: {ref}')

    # 插入 webhook_events 表
    event = WebhookEvent(
        repository_name=repo,
        repository_owner=owner,
        pusher=pusher,
        ref=ref,
        before_sha=before,
        after_sha=after,
        received_at=datetime.utcnow()
    )
    db.session.add(event)
    db.session.commit()
    logger.info(f'插入 webhook_events ID: {event.id}')

    invalid_commits = []

    for commit in commits:
        message = commit.get('message', '')
        sha = commit.get('id', '')
        author = commit.get('author', {}).get('name', '')
        url = commit.get('url', '')

        logger.info(f'验证提交 {sha} 的 DCO 签名。')

        has_dco = False
        lines = message.split('\n')
        for line in lines:
            if DCO_REGEX.match(line.strip()):
                has_dco = True
                logger.info('找到有效的 DCO 签名。')
                break

        if not has_dco:
            logger.info(f'提交 {sha} 由 {author} 创建，未通过 DCO 验证。')
            invalid_commit = InvalidCommit(
                sha=sha,
                author=author,
                message=message,
                url=url,
                detected_at=datetime.utcnow(),
                event_id=event.id
            )
            db.session.add(invalid_commit)
            invalid_commits.append(invalid_commit)

    if invalid_commits:
        db.session.commit()  # 提交 invalid_commits
        subject = Config.EMAIL_SUBJECT or f"DCO 验证失败 - 仓库 {owner}/{repo} 的新提交"
        body = Config.EMAIL_BODY_TEMPLATE or "以下提交缺少有效的 'Signed-off-by' 行，请在每次提交时使用 'git commit -s' 添加签名：\n\n"

        for commit in invalid_commits:
            body += f"Commit SHA: {commit.sha}\n"
            body += f"作者: {commit.author}\n"
            body += f"消息: {commit.message}\n"
            body += f"链接: {commit.url}\n\n"

        # 发送邮件通知
        utils.send_email(subject, body, commit.author.email)

        logger.info(f'发现 {len(invalid_commits)} 个未通过 DCO 验证的提交，已发送邮件通知。')
    else:
        logger.info('所有提交均通过 DCO 验证。')

    return jsonify({'message': 'Processed'}), 200

@app.route('/view_events', methods=['GET'])
def view_events():
    # 获取查询参数
    repository = request.args.get('repository', '').strip()
    owner = request.args.get('owner', '').strip()
    pusher = request.args.get('pusher', '').strip()
    date_from = request.args.get('date_from', '').strip()
    date_to = request.args.get('date_to', '').strip()
    invalid_commits_filter = request.args.get('invalid_commits', '').strip()  # 新增

    query = WebhookEvent.query

    if repository:
        query = query.filter(WebhookEvent.repository_name.ilike(f'%{repository}%'))
    if owner:
        query = query.filter(WebhookEvent.repository_owner.ilike(f'%{owner}%'))
    if pusher:
        query = query.filter(WebhookEvent.pusher.ilike(f'%{pusher}%'))
    if date_from:
        try:
            date_from_dt = datetime.strptime(date_from, '%Y-%m-%d')
            query = query.filter(WebhookEvent.received_at >= date_from_dt)
        except ValueError:
            pass  # 忽略格式错误的日期
    if date_to:
        try:
            date_to_dt = datetime.strptime(date_to, '%Y-%m-%d')
            query = query.filter(WebhookEvent.received_at <= date_to_dt)
        except ValueError:
            pass  # 忽略格式错误的日期

    # 处理新的无效提交筛选
    if invalid_commits_filter == 'yes':
        query = query.join(WebhookEvent.invalid_commits).filter(InvalidCommit.id.isnot(None)).distinct()
    elif invalid_commits_filter == 'no':
        query = query.outerjoin(WebhookEvent.invalid_commits).filter(InvalidCommit.id.is_(None)).distinct()

    events = query.order_by(WebhookEvent.received_at.desc()).all()
    
    # 准备筛选条件以保持在表单中
    filters = {
        'repository': repository,
        'owner': owner,
        'pusher': pusher,
        'date_from': date_from,
        'date_to': date_to,
        'invalid_commits': invalid_commits_filter
    }

    return render_template('view_events.html', events=events, filters=filters)

@app.route('/view_installed_users', methods=['GET'])
def view_installed_users():
    # 获取当前页码，默认为1
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 10, type=int)  # 每页显示10条记录

    # 可选的过滤条件
    filters = []

    # 根据请求的参数动态构建过滤条件
    if request.args.get('login'):
        filters.append(User.login.like(f"%{request.args.get('login')}%"))
    
    if request.args.get('name'):
        filters.append(User.name.like(f"%{request.args.get('name')}%"))
    
    # 获取查询结果并分页
    query = User.query
    query = query.filter(*filters)

    # 获取分页后的用户数据
    users = query
    total_pages = 1
    current_page = 1

    # 返回模板，传递分页信息
    return render_template(
        'view_installed_users.html', 
        users=users,
        page=current_page,
        total_pages=total_pages,
        per_page=per_page
    )

@app.route('/user_details/<user_id>', methods=['GET'])
def view_user_details(user_id):
    # 获取用户信息
    user = User.query.get(user_id)
    return render_template('view_user_details.html', user=user)

@app.route('/env_test')
def env_test():
    return jsonify({
        'DATABASE_URI': Config.SQLALCHEMY_DATABASE_URI,
        'WEBHOOK_SECRET': Config.WEBHOOK_SECRET
    })

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
    logger.error(f'内部服务器错误: {e}')
    return render_template('500.html'), 500

if __name__ == '__main__':
    # 在首次运行时创建数据库表
    with app.app_context():
        db.create_all()
    # 运行 Flask 应用
    app.run(host='0.0.0.0', port=5000, debug=True)
